﻿using System.Collections.Generic;
using MessageMachine;
using Microsoft.Xna.Framework.Input;

namespace InputXMachine
{
    public class InputWin : ISubscriber
    {
        private KeyboardState lastKeyboardState;
        private MouseState lastMouseState;
        /// <summary> Keys are keyboard buttons and values are msgInts.</summary>
        private Dictionary<Keys, int> keys;

        //Static only to make use of the AddKey(...) method.
        private static int msgAddKey, msgRemoveKey, msgClearKeys;

        /// <summary> Create a new input manager which uses XNA.</summary>
        /// <param name="msgAddKey">ExtraInfo is of type System.Tuple(Xna.Input.Keys, int).</param>
        /// <param name="msgRemoveKey">ExtraInfo is of type Xna.Input.Keys.</param>
        /// <param name="msgClearKeys">ExtraInfo is of type Xna.Input.Keys.</param>
        public InputWin(int msgAddKey, int msgRemoveKey, int msgClearKeys)
        {
            keys = new Dictionary<Keys, int>();
            InputWin.msgAddKey = msgAddKey;
            InputWin.msgRemoveKey = msgRemoveKey;
            InputWin.msgClearKeys = msgClearKeys;
            MessagePasser.Subscribe(new int[] { msgAddKey, msgRemoveKey, msgClearKeys }, this);
        }

        /// <summary>
        /// Updates the keyboard and sends messages to anyone subscribing to
        /// recently pressed and released keys.
        /// </summary>
        public void Update()
        {
            MouseState mouseState = Mouse.GetState();
            lastMouseState = mouseState;

            //Has to do it like this because otherwise we don't allow changes to keys.
            KeyboardState keyboardState = Keyboard.GetState();
            Keys[] ks = new Keys[keys.Count];
            keys.Keys.CopyTo(ks, 0);
            foreach (Keys k in ks)
            {
                if (keyboardState.IsKeyDown(k) && lastKeyboardState.IsKeyUp(k))
                    MessagePasser.Send(new Message(keys[k], true, this));
                else if (keyboardState.IsKeyUp(k) && lastKeyboardState.IsKeyDown(k))
                    MessagePasser.Send(new Message(keys[k], false, this));
            }
            lastKeyboardState = keyboardState;
        }

        /// <summary>
        /// Handles both sending a message to add the key and arranging a
        /// subscription for the subscriber.
        /// </summary>
        /// <param name="key">The key to add.</param>
        /// <param name="keySubscription">The msg# to be sent when the key is used.</param>
        /// <param name="subscriber">Whom to subscribe to this key.</param>
        public static void AddKey(Keys key, int keySubscription, ISubscriber subscriber)
        {
            MessagePasser.Send(new Message(msgAddKey,
                new BasicParts.Tuple<Keys, int>(key, keySubscription), subscriber));
            MessagePasser.Subscribe(keySubscription, subscriber);
        }

        #region ISubscriber Members
        public void Receive(Message msg)
        {
            if (msg.msgType == msgAddKey)
            {
                BasicParts.Tuple<Keys, int> t = (BasicParts.Tuple<Keys, int>)msg.extraInfo;
                if (!keys.ContainsKey(t.Item1)) keys.Add(t.Item1, t.Item2);
            }
            else if (msg.msgType == msgRemoveKey) //will probly cause errors due to collection change
                keys.Remove((Keys)msg.extraInfo);
            else if (msg.msgType == msgClearKeys) //will probly cause errors due to collection change
                keys.Clear();
        }
        #endregion
    }
}